﻿namespace WPFDBBrowser.Interfaces.DBTreeView
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Collections.Generic;

    /// <summary>
    /// Abstract base class for all DBTreeNodes.
    /// </summary>
    public abstract class DBTreeBase : INotifyPropertyChanged, IDBItem
    {
        #region Fields
        private string _name;
        private string _type;
        private string _toolTip;

        private bool _isSelected;
        private bool _isExpanded;
        private string _description;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DBTreeBase"/> class.
        /// </summary>
        /// <param name="type">The type.</param>
        public DBTreeBase(string type)
            : this(type, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DBTreeBase"/> class.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="parent">The parent.</param>
        public DBTreeBase(string type, IDBItem parent)
        {
            if (string.IsNullOrEmpty(type))
                throw new ArgumentNullException("type");
            this.Parent = parent;
            this.Type = type;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Gets the parent node.
        /// </summary>
        /// <typeparam name="TNode">The type of the node.</typeparam>
        /// <returns>TNode if it was found; else null.</returns>
        public TNode GetParentNode<TNode>() where TNode : DBTreeBase
        {
            return this.GetParentNode<TNode>(this.Parent);
        }

        /// <summary>
        /// Gets the parent node.
        /// </summary>
        /// <typeparam name="TNode">The type of the node.</typeparam>
        /// <param name="parent">The parent.</param>
        /// <returns></returns>
        private TNode GetParentNode<TNode>(IDBItem parent) where TNode : DBTreeBase
        {
            if (parent == null)
                return null;
            if (parent is TNode)
                return (TNode)parent;
            return this.GetParentNode<TNode>(parent.Parent);
        }
        /// <summary>
        /// Called when some of property was changed.
        /// It should be called from overriden classes when property of it was changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value was changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IDBItem Members
        /// <summary>
        /// Gets or sets a value indicating whether this instance is expanded.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is expanded; otherwise, <c>false</c>.
        /// </value>
        public bool IsExpanded
        {
            get
            {
                return _isExpanded;
            }

            set
            {
                if (!this._isExpanded.Equals(value))
                {
                    _isExpanded = value;
                    this.OnPropertyChanged("IsExpanded");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is selected.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is selected; otherwise, <c>false</c>.
        /// </value>
        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }

            set
            {
                if (!this._isSelected.Equals(value))
                {
                    _isSelected = value;
                    this.OnPropertyChanged("IsSelected");
                }
            }
        }

        /// <summary>
        /// Gets or sets the parent.
        /// </summary>
        /// <value>The parent.</value>
        public IDBItem Parent { get; private set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return _name; }
            set
            {
                if (string.IsNullOrEmpty(this._name) && string.IsNullOrEmpty(value))
                    return;
                if (string.IsNullOrEmpty(this._name) || !this._name.Equals(value))
                {
                    this._name = value;
                    this.OnPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// Gets or sets the tool tip.
        /// </summary>
        /// <value>The tool tip.</value>
        public string ToolTip
        {
            get { return this._toolTip; }
            set
            {
                if (string.IsNullOrEmpty(this._toolTip) && string.IsNullOrEmpty(value))
                    return;
                if (string.IsNullOrEmpty(this._toolTip) || !this._name.Equals(value))
                {
                    this._toolTip = value;
                    this.OnPropertyChanged("Description");
                }
            }
        }
        /// <summary>
        /// Gets or sets the type.
        /// </summary>
        /// <value>The type.</value>
        public string Type
        {
            get { return this._type; }
            set
            {
                if (string.IsNullOrEmpty(this._type) && string.IsNullOrEmpty(value))
                    return;
                if (string.IsNullOrEmpty(this._type) || !this._type.Equals(value))
                {
                    this._type = value;
                    this.OnPropertyChanged("Type");
                }
            }
        }
        /// <summary>
        /// Gets or sets the image source.
        /// </summary>
        /// <value>The image source.</value>
        public string ImageSource { get; protected set; }
        #endregion

        #region IName Members
        public virtual string Description
        {
            get
            {
                return this._description;
            }
            set
            {
                if (string.IsNullOrEmpty(this._description) && string.IsNullOrEmpty(value))
                    return;
                if (string.IsNullOrEmpty(this._description) || !this._description.Equals(value))
                {
                    this._description = value;
                    this.OnPropertyChanged("Description");
                }
            }
        }

        #endregion
    }

    public abstract class DBHierarchicalItem<TItem> : DBTreeBase, IHierarchicalDBItem<TItem>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DBTreeBase"/> class.
        /// </summary>
        /// <param name="type">The type.</param>
        public DBHierarchicalItem(string type)
            : this(type, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DBTreeBase"/> class.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="parent">The parent.</param>
        public DBHierarchicalItem(string type, IDBItem parent)
            : base(type, parent)
        {
            this.Items = new BindingList<TItem>();
        }
        #endregion

        public virtual BindingList<TItem> Items { get; private set; }
    }

    public class DBColumn : DBTreeBase, IColumn
    {
        public DBColumn(string type)
            : this(type, null)
        {
        }

        public DBColumn(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/column.png";
        }
    }

    public class DBEntity : DBHierarchicalItem<IDBItem>, IGenericContainer
    {
        public string _connectionString;
        public DBEntity(string type)
            : this(type, null)
        {
        }

        public DBEntity(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/server.png";
            this.Tables = new DBTableCollection(type, this) { Name = "Tables" };
            this.Views = new DBViewCollection(type, this) { Name = "Views" };
            this.Items.Add(this.Tables);
            this.Items.Add(this.Views);
        }

        public string ConnectionString
        {
            get { return this._connectionString; }
            set
            {
                if (string.IsNullOrEmpty(this._connectionString) && string.IsNullOrEmpty(value))
                    return;
                if (string.IsNullOrEmpty(this._connectionString) || !this._connectionString.Equals(value))
                {
                    this._connectionString = value;
                    this.OnPropertyChanged("ConnectionString");
                }
            }
        }

        /// <summary>
        /// Sets the tables.
        /// </summary>
        /// <param name="tables">The names of tables.</param>
        public void SetTables(IEnumerable<ITable> tables)
        {
            this.Tables.Items.Clear();
            foreach (ITable table in tables)
                this.Tables.AddTable(table);
            base.OnPropertyChanged("Tables");
        }
        /// <summary>
        /// Sets the views.
        /// </summary>
        /// <param name="views">The names of views.</param>
        public void SetViews(IEnumerable<IView> views)
        {
            this.Views.Items.Clear();
            foreach (IView view in views)
                this.Views.AddView(view);
            base.OnPropertyChanged("Views");
        }

        public DBTableCollection Tables { get; private set; }
        public DBViewCollection Views { get; private set; }
    }
    public class DBTableEntity : DBHierarchicalItem<IColumn>, ITable, IColumnContainer
    {
        public DBTableEntity(string type)
            : this(type, null)
        {
        }

        public DBTableEntity(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/table.png";
            this.AreColumnsLoaded = false;
        }
        
        public bool AreColumnsLoaded { get; private set; }
        public void SetColumns(IEnumerable<IColumn> columns)
        {
            this.Items.Clear();
            foreach (IColumn column in columns)
                this.Items.Add(new DBColumn(column.Type, this) { Name = column.Name, Description = column.Description });
            this.AreColumnsLoaded = true;
        }
        //public void AddColumn(IColumn column)
        //{
        //    this.Items.Add(column);
        //}
    }
    public class DBViewEntity : DBHierarchicalItem<IColumn>, IView, IColumnContainer
    {
        public DBViewEntity(string type)
            : this(type, null)
        {
        }

        public DBViewEntity(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/view.png";
            this.AreColumnsLoaded = false;
        }

        public bool AreColumnsLoaded { get; private set; }
        
        public string Query { get; set; }

        public void SetColumns(IEnumerable<IColumn> columns)
        {
            this.Items.Clear();
            foreach (IColumn column in columns)
                this.Items.Add(new DBColumn(column.Type, this) { Name = column.Name, Description = column.Description });
            this.AreColumnsLoaded = true;
        }

        //public void AddColumn(IColumn column)
        //{
        //    this.Items.Add(column);
        //}
    }
    public class DBTableCollection : DBEntityCollection<ITable>
    {
        public DBTableCollection(string type)
            : this(type, null)
        {
        }

        public DBTableCollection(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/table.png";
        }

        public void AddTable(ITable table)
        {
            base.Items.Add(new DBTableEntity(table.Type, this) { Name = table.Name });
        }
    }
    public class DBViewCollection : DBEntityCollection<IView>
    {
        public DBViewCollection(string type)
            : this(type, null)
        {
        }

        public DBViewCollection(string type, IDBItem parent)
            : base(type, parent)
        {
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/view.png";
        }

        public void AddView(IView view)
        {
            base.Items.Add(new DBViewEntity(view.Type, this)
            {
                Name = view.Name,
                Query = view.Query
            });
        }
    }
    public class DBEntityCollection<TItem> : DBHierarchicalItem<TItem>
        where TItem : IDBItem
    {
        public DBEntityCollection(string type)
            : this(type, null)
        {
        }

        public DBEntityCollection(string type, IDBItem parent)
            : base(type, parent)
        {
            //this.Items = new BindingList<TItem>();
            this.ImageSource = "/WPFDBBrowser.Interfaces;component/DBTreeView/Images/view.png";
        }

        //public new BindingList<TItem> Items { get; private set; }

        protected virtual void SetList(IList<TItem> items)
        {
            //this.Items = new BindingList<TItem>(items);
        }
    }
}
